﻿using System;
using System.Linq.Expressions;
using System.Web.Mvc;
using NUnit.Framework;
using NUnit.Framework.SyntaxHelpers;

namespace MyTesting
{
	[TestFixture]
	public class FooBarControllerSignOnFixture_Overload : ControllerTest<FooBarController>
	{
		[Test]
		public void Has_Filter_HandleError()
		{
			AssertFilterPresent<HandleErrorAttribute>();
		}

		[Test]
		public void Has_Filter_Authorise()
		{
			AssertFilterPresent<AuthorizeAttribute>();
		}

		public override Expression<Action<FooBarController>> ControllerAction
		{
			get { return (x => x.SignIn(null)); }
		}
	}

	[TestFixture]
	public class FooBarControllerSignOnFixture: ControllerTest<FooBarController>
	{

		public override Expression<Action<FooBarController>> ControllerAction
		{
			get { return (x => x.SignIn(null, null)); }
		}

		[Test]
		public void Has_Filter_HandleError()
		{
			AssertFilterPresent<HandleErrorAttribute>();
		}

		[Test]
		public void Has_Filter_Authorise()
		{
			AssertFilterPresent<AuthorizeAttribute>();
		}
	}

	public abstract class ControllerTest<TController>
		where TController : Controller, new()
	{
		private CheckFilters<TController> checkFilter = new CheckFilters<TController>();

		/// <summary>
		/// Controller action that we are testing.
		/// </summary>
		public abstract Expression<Action<TController>> ControllerAction { get; }

		protected void AssertAuthorisationFilterPresent()
		{
			AssertFilterPresent<AuthorizeAttribute>();
		}

		protected void AssertFilterPresent<TFilterAttribute>() where TFilterAttribute: FilterAttribute
		{
			var methodCall = GetMethodCall();
		
			Assert.That(checkFilter.GetAttribute<TFilterAttribute>(ControllerAction), Is.Not.Null, 
				"Expecting Filter:{0} to be present on Action:{1}", 
				typeof(TFilterAttribute),
				methodCall.Method.Name);
		}

		private MethodCallExpression GetMethodCall()
		{
			var body = ControllerAction.Body as MethodCallExpression;
			if (body == null)
				throw new InvalidOperationException("Expression must be a method call.");

			return body;
		}
	}
}